/* libgcc routines for M68HC11 & M68HC12.
   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2008, 2009
   Free Software Foundation, Inc.

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.

This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.

You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
<http://www.gnu.org/licenses/>.  */

#ifdef __HAVE_SHORT_INT__
	.mode mshort
#else
	.mode mlong
#endif

	.macro declare_near name
	.globl \name
	.type  \name,@function
	.size  \name,.Lend-\name
\name:
	.endm

#if defined(__USE_RTC__)
# define ARG(N) N+1

	.macro ret
#if defined(mc68hc12)
	rtc
#else
	jmp __return_32
#endif
	.endm

	.macro declare name
	.globl \name
	.type  \name,@function
	.size  \name,.Lend-\name
	.far   \name
\name:
	.endm

	.macro farsym name
	.far NAME
	.endm

#else
# define ARG(N) N

	.macro ret
	rts
	.endm

	.macro farsym name
	.endm

	.macro declare name
	.globl \name
	.type  \name,@function
	.size  \name,.Lend-\name
\name:
	.endm

#endif

	.sect .text
	

#define REG(NAME)			\
NAME:	.dc.w	1;			\
	.type NAME,@object ;		\
	.size NAME,2

#ifdef L_regs_min
/* Pseudo hard registers used by gcc.
   They should be located in page0.  */

	.sect .softregs
	.globl _.tmp
	.globl _.z,_.xy
REG(_.tmp)
REG(_.z)
REG(_.xy)

#endif

#ifdef L_regs_frame
	.sect .softregs
	.globl _.frame
REG(_.frame)
#endif

#ifdef L_regs_d1_2
	.sect .softregs
	.globl _.d1,_.d2
REG(_.d1)
REG(_.d2)
#endif

#ifdef L_regs_d3_4
	.sect .softregs
	.globl _.d3,_.d4
REG(_.d3)
REG(_.d4)
#endif

#ifdef L_regs_d5_6
	.sect .softregs
	.globl _.d5,_.d6
REG(_.d5)
REG(_.d6)
#endif

#ifdef L_regs_d7_8
	.sect .softregs
	.globl _.d7,_.d8
REG(_.d7)
REG(_.d8)
#endif

#ifdef L_regs_d9_16
/* Pseudo hard registers used by gcc.
   They should be located in page0.  */
	.sect .softregs
	.globl _.d9,_.d10,_.d11,_.d12,_.d13,_.d14
	.globl _.d15,_.d16
REG(_.d9)
REG(_.d10)
REG(_.d11)
REG(_.d12)
REG(_.d13)
REG(_.d14)
REG(_.d15)
REG(_.d16)

#endif

#ifdef L_regs_d17_32
/* Pseudo hard registers used by gcc.
   They should be located in page0.  */
	.sect .softregs
	.globl _.d17,_.d18,_.d19,_.d20,_.d21,_.d22
	.globl _.d23,_.d24,_.d25,_.d26,_.d27,_.d28
	.globl _.d29,_.d30,_.d31,_.d32
REG(_.d17)
REG(_.d18)
REG(_.d19)
REG(_.d20)
REG(_.d21)
REG(_.d22)
REG(_.d23)
REG(_.d24)
REG(_.d25)
REG(_.d26)
REG(_.d27)
REG(_.d28)
REG(_.d29)
REG(_.d30)
REG(_.d31)
REG(_.d32)
#endif

#ifdef L_premain
;;
;; Specific initialization for 68hc11 before the main.
;; Nothing special for a generic routine; Just enable interrupts.
;;
	declare_near	__premain
	clra
	tap	; Clear both I and X.
	rts
#endif

#ifdef L__exit
;;
;; Exit operation.  Just loop forever and wait for interrupts.
;; (no other place to go)
;; This operation is split in several pieces collected together by
;; the linker script.  This allows to support destructors at the
;; exit stage while not impacting program sizes when there is no
;; destructors.
;;
;; _exit:
;;    *(.fini0)		/* Beginning of finish code (_exit symbol).  */
;;    *(.fini1)		/* Place holder for applications.  */
;;    *(.fini2)		/* C++ destructors.  */
;;    *(.fini3)		/* Place holder for applications.  */
;;    *(.fini4)		/* Runtime exit.  */
;;
	.sect .fini0,"ax",@progbits
	.globl _exit
	.globl exit
	.weak  exit
	farsym  exit
	farsym  _exit
exit:
_exit:

	.sect .fini4,"ax",@progbits
fatal:
	cli
	wai
	bra fatal
#endif

#ifdef L_abort
;;
;; Abort operation.  This is defined for the GCC testsuite.
;;
	declare	abort

	ldd	#255		; 
#ifdef mc68hc12
	trap	#0x30
#else
	.byte 0xCD		; Generate an illegal instruction trap
	.byte 0x03		; The simulator catches this and stops.
#endif
	jmp _exit
#endif
	
#ifdef L_cleanup
;;
;; Cleanup operation used by exit().
;;
	declare	_cleanup

	ret
#endif

;-----------------------------------------
; required gcclib code
;-----------------------------------------
#ifdef L_memcpy
       declare	memcpy
       declare	__memcpy

	.weak memcpy
;;;
;;; void* memcpy(void*, const void*, size_t)
;;; 
;;; D    = dst	Pmode
;;; 2,sp = src	Pmode
;;; 4,sp = size	HImode (size_t)
;;; 
#ifdef mc68hc12
	ldx	ARG(2),sp
	ldy	ARG(4),sp
	pshd
	xgdy
	lsrd
	bcc	Start
	movb	1,x+,1,y+
Start:
	beq	Done
Loop:
	movw	2,x+,2,y+
	dbne	d,Loop
Done:
	puld
	ret
#else
	xgdy
	tsx
	ldd	ARG(4),x
	ldx	ARG(2),x	; SRC = X, DST = Y
	cpd	#0
	beq	End
	pshy
	inca			; Correction for the deca below
L0:
	psha			; Save high-counter part
L1:
	ldaa	0,x		; Copy up to 256 bytes
	staa	0,y
	inx
	iny
	decb
	bne	L1
	pula
	deca
	bne	L0
	puly			; Restore Y to return the DST
End:
	xgdy
	ret
#endif
#endif

#ifdef L_memset
       declare	memset
       declare	__memset
;;;
;;; void* memset(void*, int value, size_t)
;;; 
#ifndef __HAVE_SHORT_INT__
;;; D    = dst	Pmode
;;; 2,sp = src	SImode
;;; 6,sp = size	HImode (size_t)
	val  = ARG(5)
	size = ARG(6)
#else
;;; D    = dst	Pmode
;;; 2,sp = src	SImode
;;; 6,sp = size	HImode (size_t)
	val  = ARG(3)
	size = ARG(4)
#endif
#ifdef mc68hc12
	xgdx
	ldab	val,sp
	ldy	size,sp
	pshx
	beq	End
Loop:
	stab	1,x+
	dbne	y,Loop
End:
	puld
	ret
#else
	xgdx
	tsy
	ldab	val,y
	ldy	size,y		; DST = X, CNT = Y
	beq	End
	pshx
L0:
	stab	0,x		; Fill up to 256 bytes
	inx
	dey
	bne	L0
	pulx			; Restore X to return the DST
End:
	xgdx
	ret
#endif
#endif

#ifdef L_adddi3
	declare	___adddi3

	tsx
	xgdy
	ldd	ARG(8),x		; Add LSB
	addd	ARG(16),x
	std	6,y		; Save (carry preserved)

	ldd	ARG(6),x
	adcb	ARG(15),x
	adca	ARG(14),x
	std	4,y

	ldd	ARG(4),x
	adcb	ARG(13),x
	adca	ARG(12),x
	std	2,y
	
	ldd	ARG(2),x
	adcb	ARG(11),x		; Add MSB
	adca	ARG(10),x
	std	0,y

	xgdy
	ret
#endif

#ifdef L_subdi3
	declare	___subdi3

	tsx
	xgdy
	ldd	ARG(8),x		; Subtract LSB
	subd	ARG(16),x
	std	6,y			; Save, borrow preserved

	ldd	ARG(6),x
	sbcb	ARG(15),x
	sbca	ARG(14),x
	std	4,y

	ldd	ARG(4),x
	sbcb	ARG(13),x
	sbca	ARG(12),x
	std	2,y
	
	ldd	ARG(2),x		; Subtract MSB
	sbcb	ARG(11),x
	sbca	ARG(10),x
	std	0,y

	xgdy			;
	ret
#endif
	
#ifdef L_notdi2
	declare	___notdi2

	tsy
	xgdx
	ldd	ARG(8),y
	coma
	comb
	std	6,x
	
	ldd	ARG(6),y
	coma
	comb
	std	4,x

	ldd	ARG(4),y
	coma
	comb
	std	2,x

	ldd	ARG(2),y
	coma
	comb
	std	0,x
	xgdx
	ret
#endif
	
#ifdef L_negsi2
	declare_near ___negsi2

	comb
	coma
	xgdx
	comb
	coma
	inx
	xgdx
	bne	done
	inx
done:
	rts
#endif

#ifdef L_one_cmplsi2
	declare_near ___one_cmplsi2

	comb
	coma
	xgdx
	comb
	coma
	xgdx
	rts
#endif
	
#ifdef L_ashlsi3
	declare_near ___ashlsi3

	xgdy
	clra
	andb	#0x1f
	xgdy
	beq	Return
Loop:
	lsld
	xgdx
	rolb
	rola
	xgdx
	dey
	bne	Loop
Return:
	rts
#endif

#ifdef L_ashrsi3
	declare_near ___ashrsi3

	xgdy
	clra
	andb	#0x1f
	xgdy
	beq	Return
Loop:
	xgdx
	asra
	rorb
	xgdx
	rora
	rorb
	dey
	bne	Loop
Return:
	rts
#endif

#ifdef L_lshrsi3
	declare_near ___lshrsi3

	xgdy
	clra
	andb	#0x1f
	xgdy
	beq	Return
Loop:
	xgdx
	lsrd
	xgdx
	rora
	rorb
	dey
	bne	Loop
Return:
	rts
#endif

#ifdef L_lshrhi3
	declare_near ___lshrhi3

	cpx	#16
	bge	Return_zero
	cpx	#0
	beq	Return
Loop:
	lsrd
	dex
	bne	Loop
Return:
	rts
Return_zero:
	clra
	clrb
	rts
#endif
	
#ifdef L_lshlhi3
	declare_near ___lshlhi3

	cpx	#16
	bge	Return_zero
	cpx	#0
	beq	Return
Loop:
	lsld
	dex
	bne	Loop
Return:
	rts
Return_zero:
	clra
	clrb
	rts
#endif

#ifdef L_rotrhi3
	declare_near ___rotrhi3

___rotrhi3:
	xgdx
	clra
	andb	#0x0f
	xgdx
	beq	Return
Loop:
	tap
	rorb
	rora
	dex
	bne	Loop
Return:
	rts
#endif

#ifdef L_rotlhi3
	declare_near ___rotlhi3

___rotlhi3:
	xgdx
	clra
	andb	#0x0f
	xgdx
	beq	Return
Loop:
	asrb
	rolb
	rola
	rolb
	dex
	bne	Loop
Return:
	rts
#endif

#ifdef L_ashrhi3
	declare_near ___ashrhi3

	cpx	#16
	bge	Return_minus_1_or_zero
	cpx	#0
	beq	Return
Loop:
	asra
	rorb
	dex
	bne	Loop
Return:
	rts
Return_minus_1_or_zero:
	clrb
	tsta
	bpl	Return_zero
	comb
Return_zero:
	tba
	rts
#endif
	
#ifdef L_ashrqi3
	declare_near ___ashrqi3

	cmpa	#8
	bge	Return_minus_1_or_zero
	tsta
	beq	Return
Loop:
	asrb
	deca
	bne	Loop
Return:
	rts
Return_minus_1_or_zero:
	clrb
	tstb
	bpl	Return_zero
	coma
Return_zero:
	tab
	rts
#endif

#ifdef L_lshlqi3
	declare_near ___lshlqi3

	cmpa	#8
	bge	Return_zero
	tsta
	beq	Return
Loop:
	lslb
	deca
	bne	Loop
Return:
	rts
Return_zero:
	clrb
	rts
#endif

#ifdef L_divmodhi4
#ifndef mc68hc12
/* 68HC12 signed divisions are generated inline (idivs).  */

	declare_near __divmodhi4

;
;; D = numerator
;; X = denominator
;;
;; Result:	D = D / X
;;		X = D % X
;; 
	tsta
	bpl	Numerator_pos
	comb			; D = -D <=> D = (~D) + 1
	coma
	xgdx
	inx
	tsta
	bpl	Numerator_neg_denominator_pos
Numerator_neg_denominator_neg:
	comb			; X = -X
	coma
	addd	#1
	xgdx
	idiv
	coma
	comb
	xgdx			; Remainder <= 0 and result >= 0
	inx
	rts

Numerator_pos_denominator_pos:
	xgdx
	idiv
	xgdx			; Both values are >= 0
	rts
	
Numerator_pos:
	xgdx
	tsta
	bpl	Numerator_pos_denominator_pos
Numerator_pos_denominator_neg:
	coma			; X = -X
	comb
	xgdx
	inx
	idiv
	xgdx			; Remainder >= 0 but result <= 0
	coma
	comb
	addd	#1
	rts
	
Numerator_neg_denominator_pos:
	xgdx
	idiv
	coma			; One value is > 0 and the other < 0
	comb			; Change the sign of result and remainder
	xgdx
	inx
	coma
	comb
	addd	#1
	rts
#endif /* !mc68hc12 */
#endif

#ifdef L_mulqi3
	declare_near ___mulqi3

;
; short __mulqi3(signed char a, signed char b);
;
;	signed char a	-> register A
;	signed char b	-> register B
;
; returns the signed result of A * B in register D.
;
	tsta
	bmi	A_neg
	tstb
	bmi	B_neg
	mul
	rts
B_neg:
	negb
	bra	A_or_B_neg
A_neg:
	nega
	tstb
	bmi	AB_neg
A_or_B_neg:
	mul
	coma
	comb
	addd	#1
	rts
AB_neg:
	negb
	mul
	rts
#endif
	
#ifdef L_mulhi3
	declare_near ___mulhi3

;
;
;  unsigned short ___mulhi3(unsigned short a, unsigned short b)
;
;	a = register D
;	b = register X
;
#ifdef mc68hc12
	pshx			; Preserve X
	exg	x,y
	emul
	exg	x,y
	pulx
	rts
#else
#ifdef NO_TMP
	;
	; 16-bit multiplication without temp memory location.
	; (smaller but slower)
	;
	pshx			; (4)
	ins			; (3)
	pshb			; (3)
	psha			; (3)
	pshx			; (4)
	pula			; (4)
	pulx			; (5)
	mul			; (10) B.high * A.low
	xgdx			; (3)
	mul			; (10) B.low * A.high
	abx			; (3)
	pula			; (4)
	pulb			; (4)
	mul			; (10) B.low * A.low
	pshx			; (4) 
	tsx			; (3)
	adda	1,x		; (4)
	pulx			; (5)
	rts			; (5) 20 bytes
				; ---
				; 91 cycles
#else
	stx	*_.tmp		; (4)
	pshb			; (3)
	ldab	*_.tmp+1	; (3)
	mul			; (10) A.high * B.low
	ldaa	*_.tmp		; (3)
	stab	*_.tmp		; (3)
	pulb			; (4)
	pshb			; (4)
	mul			; (10) A.low * B.high
	addb	*_.tmp		; (4)
	stab	*_.tmp		; (3)
	ldaa	*_.tmp+1	; (3)
	pulb			; (4)
	mul			; (10) A.low * B.low
	adda	*_.tmp		; (4)
	rts			; (5) 24/32 bytes
				; 77/85 cycles
#endif
#endif
#endif

#ifdef L_mulhi32

;
;
;  unsigned long __mulhi32(unsigned short a, unsigned short b)
;
;	a = register D
;	b = value on stack
;
;	+---------------+
;       |  B low	| <- 7,x
;	+---------------+
;       |  B high	| <- 6,x
;	+---------------+
;       |  PC low	|  
;	+---------------+
;       |  PC high	|  
;	+---------------+
;	|  Tmp low	|
;	+---------------+
;	|  Tmp high     |
;	+---------------+
;	|  A low	|
;	+---------------+
;	|  A high	|
;	+---------------+  <- 0,x
;
;
;      <B-low>    5,x
;      <B-high>   4,x
;      <ret>      2,x
;      <A-low>    1,x
;      <A-high>   0,x
;
	declare_near	__mulhi32

#ifdef mc68hc12
	ldy	2,sp
	emul
	exg	x,y
	rts
#else
	pshx			; Room for temp value
	pshb
	psha
	tsx
	ldab	6,x
	mul
	xgdy			; A.high * B.high
	ldab	7,x
	pula
	mul			; A.high * B.low
	std	2,x
	ldaa	1,x
	ldab	6,x
	mul			; A.low * B.high
	addd	2,x
	stab	2,x
	tab
	aby
	bcc	N
	ldab	#0xff
	aby
	iny
N:
	ldab	7,x
	pula
	mul			; A.low * B.low
	adda	2,x
	pulx			; Drop temp location
	pshy			; Put high part in X
	pulx
	bcc	Ret
	inx
Ret:
	rts
#endif	
#endif

#ifdef L_mulsi3

;
;      <B-low>    8,y
;      <B-high>   6,y
;      <ret>      4,y
;	<tmp>	  2,y
;      <A-low>    0,y
;
; D,X   -> A
; Stack -> B
;
; The result is:
;
;	(((A.low * B.high) + (A.high * B.low)) << 16) + (A.low * B.low)
;
;
;

	declare	__mulsi3

#ifdef mc68hc12
	pshd				; Save A.low
	ldy	ARG(4),sp
	emul				; A.low * B.high
	ldy	ARG(6),sp
	exg	x,d
	emul				; A.high * B.low
	leax	d,x
	ldy	ARG(6),sp
	puld
	emul				; A.low * B.low
	exg	d,y
	leax	d,x
	exg	d,y
	ret
#else
B_low	=	ARG(8)
B_high	=	ARG(6)
A_low	=	0
A_high	=	2
	pshx
	pshb
	psha
	tsy
;
; If B.low is 0, optimize into: (A.low * B.high) << 16
;
	ldd	B_low,y
	beq	B_low_zero
;
; If A.high is 0, optimize into: (A.low * B.high) << 16 + (A.low * B.low)
;
	cpx	#0
	beq	A_high_zero
	bsr	___mulhi3		; A.high * B.low
;
; If A.low is 0, optimize into: (A.high * B.low) << 16
;
	ldx	A_low,y
	beq	A_low_zero		; X = 0, D = A.high * B.low
	std	2,y
;
; If B.high is 0, we can avoid the (A.low * B.high) << 16 term.
;
	ldd	B_high,y
	beq	B_high_zero
	bsr	___mulhi3		; A.low * B.high
	addd	2,y
	std	2,y
;
; Here, we know that A.low and B.low are not 0.
;
B_high_zero:
	ldd	B_low,y			; A.low is on the stack
	bsr	__mulhi32		; A.low * B.low
	xgdx
	tsy				; Y was clobbered, get it back
	addd	2,y
A_low_zero:				; See A_low_zero_non_optimized below
	xgdx
Return:
	ins
	ins
	ins
	ins
	ret
;
; 
; A_low_zero_non_optimized:
;
; At this step, X = 0 and D = (A.high * B.low)
; Optimize into: (A.high * B.low) << 16
;
;	xgdx
;	clra			; Since X was 0, clearing D is superfuous.
;	clrb
;	bra	Return
; ----------------
; B.low == 0, the result is:	(A.low * B.high) << 16
;
; At this step:
;   D = B.low				= 0 
;   X = A.high				?
;       A.low is at A_low,y		?
;       B.low is at B_low,y		?
;
B_low_zero:
	ldd	A_low,y
	beq	Zero1
	ldx	B_high,y
	beq	Zero2
	bsr	___mulhi3
Zero1:
	xgdx
Zero2:
	clra
	clrb
	bra	Return
; ----------------
; A.high is 0, optimize into: (A.low * B.high) << 16 + (A.low * B.low)
;
; At this step:
;   D = B.low				!= 0 
;   X = A.high				= 0
;       A.low is at A_low,y		?
;       B.low is at B_low,y		?
;
A_high_zero:
	ldd	A_low,y		; A.low
	beq	Zero1
	ldx	B_high,y	; B.high
	beq	A_low_B_low
	bsr	___mulhi3
	std	2,y
	bra	B_high_zero	; Do the (A.low * B.low) and the add.

; ----------------
; A.high and B.high are 0 optimize into: (A.low * B.low)
;
; At this step:
;   D = B.high				= 0 
;   X = A.low				!= 0
;       A.low is at A_low,y		!= 0
;       B.high is at B_high,y		= 0
;
A_low_B_low:
	ldd	B_low,y			; A.low is on the stack
	bsr	__mulhi32
	bra	Return
#endif
#endif

#ifdef L_map_data

	.sect	.install2,"ax",@progbits
	.globl	__map_data_section
	.globl __data_image
#ifdef mc68hc12
	.globl __data_section_size
#endif
__map_data_section:
#ifdef mc68hc12
	ldx	#__data_image
	ldy	#__data_section_start
	ldd	#__data_section_size
	beq	Done
Loop:
	movb	1,x+,1,y+
	dbne	d,Loop
#else
	ldx	#__data_image
	ldy	#__data_section_start
	bra	Start_map
Loop:
	ldaa	0,x
	staa	0,y
	inx
	iny
Start_map:
	cpx	#__data_image_end
	blo	Loop
#endif
Done:

#endif

#ifdef L_init_bss

	.sect	.install2,"ax",@progbits
	.globl	__init_bss_section

__init_bss_section:
	ldd	#__bss_size
	beq	Done
	ldx	#__bss_start
Loop:
#ifdef mc68hc12
	clr	1,x+
	dbne	d,Loop
#else
	clr	0,x
	inx
	subd	#1
	bne	Loop
#endif
Done:

#endif

#ifdef L_ctor

; End of constructor table
	.sect	.install3,"ax",@progbits
	.globl	__do_global_ctors

__do_global_ctors:
	; Start from the end - sizeof(void*)
	ldx	#__CTOR_END__-2
ctors_loop:
	cpx	#__CTOR_LIST__
	blo	ctors_done
	pshx
	ldx	0,x
	jsr	0,x
	pulx
	dex
	dex
	bra	ctors_loop
ctors_done:

#endif

#ifdef L_dtor

	.sect	.fini3,"ax",@progbits
	.globl	__do_global_dtors

;;
;; This piece of code is inserted in the _exit() code by the linker.
;;
__do_global_dtors:
	pshb	; Save exit code
	psha
	ldx	#__DTOR_LIST__
dtors_loop:
	cpx	#__DTOR_END__
	bhs	dtors_done
	pshx
	ldx	0,x
	jsr	0,x
	pulx
	inx
	inx
	bra	dtors_loop
dtors_done:
	pula	; Restore exit code
	pulb

#endif

#ifdef L_far_tramp
#ifdef mc68hc12
	.sect	.tramp,"ax",@progbits
	.globl	__far_trampoline

;; This is a trampoline used by the linker to invoke a function
;; using rtc to return and being called with jsr/bsr.
;; The trampoline generated is:
;;
;;	foo_tramp:
;;		ldy	#foo
;;		call	__far_trampoline,page(foo)
;;
;; The linker transforms:
;;
;;		jsr	foo
;;
;; into
;;		jsr	foo_tramp
;;
;; The linker generated trampoline and _far_trampoline must be in 
;; non-banked memory.
;;
__far_trampoline:
	movb	0,sp, 2,sp	; Copy page register below the caller's return
	leas	2,sp		; address.
	jmp	0,y		; We have a 'call/rtc' stack layout now
				; and can jump to the far handler
				; (whose memory bank is mapped due to the
				; call to the trampoline).
#endif

#ifdef mc68hc11
	.sect	.tramp,"ax",@progbits
	.globl __far_trampoline

;; Trampoline generated by gcc for 68HC11:
;;
;;	pshb
;;	ldab	#%page(func)
;;	ldy	#%addr(func)
;;	jmp	__far_trampoline
;;
__far_trampoline:
	psha				; (2) Save function parameter (high)
	;; <Read current page in A>
	psha				; (2)
	;; <Set currenge page from B>
	pshx				; (4)
	tsx				; (3)
	ldab	4,x			; (4) Restore function parameter (low)
	ldaa	2,x			; (4) Get saved page number
	staa	4,x			; (4) Save it below return PC
	pulx				; (5)
	pula				; (3)
	pula				; (3) Restore function parameter (high)
	jmp	0,y			; (4)
#endif
#endif

#ifdef L_call_far
#ifdef mc68hc11
	.sect	.tramp,"ax",@progbits
	.globl __call_a16
	.globl __call_a32
;;
;; The call methods are used for 68HC11 to support memory bank switching.
;; Every far call is redirected to these call methods.  Its purpose is to:
;;
;;  1/ Save the current page on the stack (1 byte to follow 68HC12 call frame)
;;  2/ Install the new page
;;  3/ Jump to the real function
;;
;; The page switching (get/save) is board dependent.  The default provided
;; here does nothing (just create the appropriate call frame).
;;
;; Call sequence (10 bytes, 13 cycles):
;;
;;	ldx #page			; (3)
;;	ldy #func			; (4)
;;	jsr __call_a16			; (6)
;;
;; Call trampoline (11 bytes, 19 cycles):
;;
__call_a16:
	;; xgdx				; (3)
	;; <Read current page in A>	; (3) ldaa _current_page
	psha				; (2)
	;; <Set current page from B>	; (4) staa _current_page
	;; xgdx				; (3)
	jmp 0,y				; (4)

;;
;; Call sequence (10 bytes, 14 cycles):
;;
;;	pshb				; (2)
;;	ldab #page			; (2)
;;	ldy  #func			; (4)
;;	jsr __call_a32			; (6)
;;
;; Call trampoline (87 bytes, 57 cycles):
;;
__call_a32:
	pshx				; (4)
	psha				; (2)
	;; <Read current page in A>	; (3) ldaa _current_page
	psha				; (2)
	;; <Set current page from B>	; (4) staa _current_page
	tsx				; (3)
	ldab	6,x			; (4) Restore function parameter
	ldaa	5,x			; (4) Move PC return at good place
	staa	6,x			; (4)
	ldaa	4,x			; (4)
	staa	5,x			; (4)
	pula				; (3)
	staa	4,x			; (4)
	pula				; (3)
	pulx				; (5)
	jmp	0,y			; (4)
#endif
#endif

#ifdef L_return_far
#ifdef mc68hc11
	.sect	.tramp,"ax",@progbits
       .globl __return_void
       .globl __return_16
       .globl __return_32

__return_void:
	;; pulb
	;; <Set current page from B> (Board specific)
	;; rts
__return_16:
	;; xgdx
	;; pulb
	;; <Set current page from B> (Board specific)
	;; xgdx
	;; rts
__return_32:
	;; xgdy
	;; pulb
	;; <Set current page from B> (Board specific)
	;; xgdy
	;; rts
	ins
	rts
#endif
#endif
.Lend:
;-----------------------------------------
; end required gcclib code
;-----------------------------------------
